package ye.guo.huang.fs.client;

import org.apache.commons.io.FileUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClients;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.*;
import java.net.URLDecoder;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @Author: HUANG GY.
 * @Date: 2021/10/11 14:33
 * @Description: 分片下载的客户端
 */
@RestController
@RequestMapping("/api")
public class DownloadClient {

    /**
     * 每个分片的大小
     */
    private final static long PER_PAGE = 1024L * 1024L * 5L;

    /**
     *  文件下载地址  分片临时存放地址
     */
    private final static String PATH = "D:\\05_code\\file-server\\fileItemDown";
    ExecutorService pool = Executors.newFixedThreadPool(10);


    @RequestMapping("/downloadFile")
    public String downloadFile() throws Exception {
        //探测，获取文件信息
        FileInfo fileInfo = download(0, 10, -1, null);
        if (fileInfo != null) {
            long fSize = fileInfo.fSize;
            System.out.println("all size="+fSize);
            String fName = fileInfo.fName;
            long pages = fSize / PER_PAGE ;
            //  <=
            for (long i = 0; i <= pages; i++) {
                pool.submit(new DownLoad(i*PER_PAGE, (i+1)*PER_PAGE -1,  i, fName));
            }
        }

        return "success";
    }

    class FileInfo{
        long fSize;
        String fName ;

        public FileInfo(long fSize, String fName) {
            this.fSize = fSize;
            this.fName = fName;
        }
    }

    class DownLoad implements Runnable{
        long start ;
        long end;
        long page;
        String fName;

        public DownLoad(long start, long end, long page, String fName) {
            this.start = start;
            this.end = end;
            this.page = page;
            this.fName = fName;
        }

        public void run() {
            try {
                download(start, end, page, fName);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     *  分片下载和探测
     * @param start 开始位置
     * @param end 结束位置
     * @param page 分片大小
     * @param fName 文件名字
     * @return
     */
    private FileInfo download(long start ,long end,long page,String fName) throws Exception {
        System.out.println(page + "--" + fName + "--" + start + "--" + end);
        File file = new File(PATH,page+"-"+fName);
        // 断点下载
        // page == -1是探测
        //file.length() == PER_PAGE 下载到一半
        if (file.exists() && page != -1 && file.length() == PER_PAGE) {
            return null ;
        }
        HttpClient client = HttpClients.createDefault();
        HttpGet httpGet = new HttpGet("http://127.0.0.1:8080/api/down");
        //告诉服务段做分片下载
        httpGet.setHeader("Range","bytes="+start+"-"+end);
        try {
            HttpResponse response = client.execute(httpGet);
            String fSize = response.getFirstHeader("fSize").getValue();
            fName = response.getFirstHeader("fName").getValue();
            fName = URLDecoder.decode(fName,"utf-8");
            HttpEntity entity = response.getEntity();
            InputStream is = entity.getContent();
            //临时存储分片文件
            FileOutputStream fos = new FileOutputStream(file);
            byte [] buffer = new byte[1024];
            int ch ;
            while ((ch = is.read(buffer))!=-1){
                fos.write(buffer,0,ch);
            }
            fos.flush();
            fos.close();


            if (end - Long.parseLong(fSize) > 0){
                mergeFile(fName,page);
            }


            return new FileInfo(Long.parseLong(fSize),fName);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null ;
    }

    /**
     *  分片文件下载
     * @param fName
     * @param page
     * @throws Exception
     */
    private void mergeFile(String fName, long page) throws Exception {
        File file = new File(PATH,fName);
        BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(file));
        //文件合并 <= 最后一个分片
        for (int i = 0; i <= page; i++) {
            File tempFile = new File(PATH,i + "-" + fName);
            //最后一个分片上传后，进行文件合并，但是此时多线程情况下并不保证其它分片已经来了
            int j = 0 ;
            while (!tempFile.exists() && j < 100000  || (i!=page && tempFile.length() < PER_PAGE)) {
                //文件不存在，进入循环
                Thread.sleep(100);
                //有的分配如果始终传不上来，这边不能始终进行阻塞
                j++ ;
            }
            //读取这个分片
            byte[] bytes = FileUtils.readFileToByteArray(tempFile);
            os.write(bytes);
            os.flush();
            //删除临时文件
            tempFile.delete();
        }
        //删除探测文件
        new File(PATH,-1+"-null").delete();
        os.flush();
        os.close();
    }

}
